////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   particledialog.cpp
//  Version:     v1.00
//  Created:     17/6/2003 by Timur.
//  Compilers:   Visual Studio.NET
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "ParticleDialog.h"

#include "StringDlg.h"
#include "NumberDlg.h"

#include "ParticleManager.h"
#include "ParticleLibrary.h"
#include "ParticleItem.h"

#include "Objects\BrushObject.h"
#include "Objects\EntityObject.h"
#include "ViewManager.h"
#include "Clipboard.h"

#include <I3DEngine.h>
#include <ParticleParams.h>
#include <CryTypeInfo.h>
#include <IEntitySystem.h>

#include "Console/ConsoleSync.h"

#include "Util\VariableTypeInfo.h"

#include "ParticleParams_TypeInfo.h"

#define IDC_PARTICLES_TREE AFX_IDW_PANE_FIRST

#define EDITOR_OBJECTS_PATH CString("Editor/Objects/")

IMPLEMENT_DYNAMIC(CParticleDialog,CBaseLibraryDialog);
//////////////////////////////////////////////////////////////////////////
// Particle UI structures.
//////////////////////////////////////////////////////////////////////////

/** User Interface definition of particle system.
*/
class CParticleUIDefinition
{
public:
	ParticleParams m_localParams;
	CVarBlockPtr m_vars;
	IVariable::OnSetCallback m_onSetCallback;

	//////////////////////////////////////////////////////////////////////////
	CVarBlock* CreateVars()
	{
		m_vars = new CVarBlock;

		int nNumGroups = 0;

		// Create UI vars, using ParticleParams TypeInfo.
		CVariableArray* pVarTable = 0;

		static ParticleParams s_defaultParams;

		const CTypeInfo& partInfo = TypeInfo(&m_localParams);
		for AllSubVars( pVarInfo, partInfo )
		{
			if (*pVarInfo->GetName() == '_')
				continue;

			string sGroup;
			if (pVarInfo->GetAttr("Group", sGroup))
			{
				pVarTable = AddGroup(sGroup);
				if (nNumGroups++ > 5)
				{
					pVarTable ->SetFlags( pVarTable ->GetFlags()|IVariable::UI_ROLLUP2 );
				}
				if (pVarInfo->Type.IsType<void>())
					continue;
			}

			IVariable* pVar = CVariableTypeInfo::Create(*pVarInfo, &m_localParams, &s_defaultParams);

			// Add to group.
			pVar->AddRef();								// Variables are local and must not be released by CVarBlock.
			if (pVarTable)
				pVarTable->AddChildVar(pVar);
		}

		return m_vars;
	}

	//////////////////////////////////////////////////////////////////////////
	void SetFromParticles( CParticleItem *pParticles )
	{
		IParticleEffect *pEffect = pParticles->GetEffect();
		if (!pEffect)
			return;

		// Copy to local params, then run update on all vars.
		m_localParams = pEffect->GetParticleParams();
		m_vars->OnSetValues();
	}

	//////////////////////////////////////////////////////////////////////////
	void SetToParticles( CParticleItem *pParticles )
	{
		IParticleEffect *pEffect = pParticles->GetEffect();
		if (!pEffect)
			return;

		pEffect->SetParticleParams(m_localParams);

		// Update particles.
		pParticles->Update();
	}

private:

	//////////////////////////////////////////////////////////////////////////
  CVariableArray* AddGroup( const char *sName )
	{
		CVariableArray* pArray = new CVariableArray;
		pArray->AddRef();
		pArray->SetFlags(IVariable::UI_BOLD);
		if (sName)
			pArray->SetName(sName);
		m_vars->AddVariable(pArray);
		return pArray;
	}
};

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
class CParticlePickCallback : public IPickObjectCallback
{
public:
	CParticlePickCallback() { m_bActive = true; };
	//! Called when object picked.
	virtual void OnPick( CBaseObject *picked )
	{
		/*
		m_bActive = false;
		CParticleItem *pParticles = picked->GetParticle();
		if (pParticles)
			GetIEditor()->OpenParticleLibrary( pParticles );
			*/
		delete this;
	}
	//! Called when pick mode cancelled.
	virtual void OnCancelPick()
	{
		m_bActive = false;
		delete this;
	}
	//! Return true if specified object is pickable.
	virtual bool OnPickFilter( CBaseObject *filterObject )
	{
		/*
		// Check if object have material.
		if (filterObject->GetParticle())
			return true;
		*/
		return false;
	}
	static bool IsActive() { return m_bActive; };
private:
	static bool m_bActive;
};
bool CParticlePickCallback::m_bActive = false;
//////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////
// CParticleDialog implementation.
//////////////////////////////////////////////////////////////////////////
CParticleDialog::CParticleDialog( CWnd *pParent )
	: CBaseLibraryDialog(IDD_DB_ENTITY, pParent)
{
	m_pPartManager = GetIEditor()->GetParticleManager();
	m_pItemManager = m_pPartManager;

	m_bRealtimePreviewUpdate = true;
	m_sortRecursionType = SORT_RECURSION_ITEM;

	m_dragImage = 0;
	m_hDropItem = 0;

	m_bForceReloadPropsCtrl = true;

	m_hCursorDefault = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
	m_hCursorCreate = AfxGetApp()->LoadCursor( IDC_HIT_CURSOR );
	m_hCursorReplace = AfxGetApp()->LoadCursor(IDC_HAND_INTERNAL);

	pParticleUI = new CParticleUIDefinition;

	// Immidiatly create dialog.
	Create( IDD_DATABASE,pParent );
}

CParticleDialog::~CParticleDialog()
{
	delete pParticleUI;	
}

void CParticleDialog::DoDataExchange(CDataExchange* pDX)
{
	CBaseLibraryDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CParticleDialog, CBaseLibraryDialog)
	ON_COMMAND( ID_DB_ADD,OnAddItem )
	ON_COMMAND( ID_DB_PLAY,OnPlay )

	ON_UPDATE_COMMAND_UI( ID_DB_PLAY,OnUpdatePlay )

	ON_COMMAND( ID_DB_SELECTASSIGNEDOBJECTS,OnSelectAssignedObjects )
	ON_COMMAND( ID_DB_ACTIVATE,OnEnable )
	ON_COMMAND( ID_DB_ACTIVATE_ALL,OnEnableAll )
	ON_COMMAND( ID_DB_ASSIGNTOSELECTION,OnAssignParticleToSelection )
	ON_COMMAND( ID_DB_GETFROMSELECTION,OnGetParticleFromSelection )
	ON_COMMAND( ID_DB_MTL_RESETMATERIAL,OnResetParticleOnSelection )
	ON_COMMAND( ID_DB_PLAY,OnEntityStart)
	ON_COMMAND( ID_DB_STOP,OnEntityStop)
	ON_UPDATE_COMMAND_UI( ID_DB_ASSIGNTOSELECTION,OnUpdateAssignToSelection )
	ON_UPDATE_COMMAND_UI( ID_DB_SELECTASSIGNEDOBJECTS,OnUpdateSelected )
	ON_UPDATE_COMMAND_UI( ID_DB_GETFROMSELECTION,OnUpdateObjectSelected )
	ON_UPDATE_COMMAND_UI( ID_DB_MTL_RESETMATERIAL,OnUpdateObjectSelected )

	ON_COMMAND( ID_DB_MTL_PICK,OnPick )
	ON_UPDATE_COMMAND_UI( ID_DB_MTL_PICK,OnUpdatePick )

	ON_NOTIFY(TVN_KEYDOWN, IDC_LIBRARY_ITEMS_TREE, OnKeyDown)
	ON_NOTIFY(TVN_BEGINDRAG, IDC_PARTICLES_TREE, OnBeginDrag)
	ON_NOTIFY(NM_RCLICK , IDC_PARTICLES_TREE, OnNotifyTreeRClick)
	ON_NOTIFY(NM_CLICK, IDC_PARTICLES_TREE, OnNotifyTreeLClick)
	ON_WM_SIZE()
	ON_WM_DESTROY()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONUP()
END_MESSAGE_MAP()

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnDestroy()
{
	int temp;
	int HSplitter,VSplitter;
	m_wndHSplitter.GetRowInfo( 0,HSplitter,temp );
	m_wndVSplitter.GetColumnInfo( 0,VSplitter,temp );
	AfxGetApp()->WriteProfileInt("Dialogs\\Particles","HSplitter",HSplitter );
	AfxGetApp()->WriteProfileInt("Dialogs\\Particles","VSplitter",VSplitter );

	//ReleaseGeometry();
	CBaseLibraryDialog::OnDestroy();
}

// CTVSelectKeyDialog message handlers
BOOL CParticleDialog::OnInitDialog()
{
	CBaseLibraryDialog::OnInitDialog();

	InitToolbar(IDR_DB_PARTICLES_BAR);

	CRect rc;
	GetClientRect(rc);

	// Create left panel tree control.
	m_treeCtrl.Create( WS_VISIBLE|WS_CHILD|WS_TABSTOP|WS_BORDER|TVS_HASBUTTONS|TVS_SHOWSELALWAYS|TVS_LINESATROOT|TVS_HASLINES|
		TVS_FULLROWSELECT|TVS_NOHSCROLL|TVS_INFOTIP/*|TVS_TRACKSELECT*/,rc,this,IDC_LIBRARY_ITEMS_TREE );
	if (!gSettings.gui.bWindowsVista)
		m_treeCtrl.SetItemHeight(18);

	//int h2 = rc.Height()/2;
	int h2 = 200;

	int HSplitter = AfxGetApp()->GetProfileInt("Dialogs\\Particles","HSplitter",200 );
	int VSplitter = AfxGetApp()->GetProfileInt("Dialogs\\Particles","VSplitter",200 );
	//int HSplitter = 300;

	m_wndVSplitter.CreateStatic( this,1,2,WS_CHILD|WS_VISIBLE );
	m_wndHSplitter.CreateStatic( &m_wndVSplitter,2,1,WS_CHILD|WS_VISIBLE );

	//m_imageList.Create(IDB_PARTICLES_TREE, 16, 1, RGB (255, 0, 255));
	CMFCUtils::LoadTrueColorImageList( m_imageList,IDB_PARTICLES_TREE,16,RGB(255,0,255) );

	// TreeCtrl must be already created.
	m_treeCtrl.SetParent( &m_wndHSplitter );
	m_treeCtrl.SetImageList(&m_imageList,TVSIL_NORMAL);

	m_previewCtrl.Create( &m_wndHSplitter,rc,WS_CHILD|WS_VISIBLE );
	m_previewCtrl.SetGrid(true);
	m_previewCtrl.SetAxis(true);
	m_previewCtrl.EnableUpdate( true );

	m_propsCtrl.Create( WS_VISIBLE|WS_CHILD|WS_BORDER,rc,&m_wndVSplitter,2 );
	m_vars = pParticleUI->CreateVars();
	m_propsCtrl.AddVarBlock( m_vars );
	m_propsCtrl.ExpandAllChilds( m_propsCtrl.GetRootItem(),false );
	m_propsCtrl.EnableWindow( FALSE );

	//m_wndHSplitter.SetPane( 0,0,&m_previewCtrl,CSize(100,HSplitter) );
	//m_wndHSplitter.SetPane( 1,0,&m_propsCtrl,CSize(100,HSplitter) );

	//m_wndVSplitter.SetPane( 0,0,&m_treeCtrl,CSize(VSplitter,100) );
	//m_wndVSplitter.SetPane( 0,1,&m_wndHSplitter,CSize(VSplitter,100) );

	m_wndHSplitter.SetPane( 0,0,&m_treeCtrl,CSize(100,HSplitter) );
	m_wndHSplitter.SetPane( 1,0,&m_previewCtrl,CSize(100,HSplitter) );

	m_wndVSplitter.SetPane( 0,0,&m_wndHSplitter,CSize(VSplitter,100) );
	m_wndVSplitter.SetPane( 0,1,&m_propsCtrl,CSize(VSplitter,100) );

	RecalcLayout();

	ReloadLibs();

	return TRUE;  // return TRUE unless you set the focus to a control
	// EXCEPTION: OCX Property Pages should return FALSE
}

//////////////////////////////////////////////////////////////////////////
UINT CParticleDialog::GetDialogMenuID()
{
	return IDR_DB_ENTITY;
};

//////////////////////////////////////////////////////////////////////////
// Create the toolbar
void CParticleDialog::InitToolbar( UINT nToolbarResID )
{
	InitLibraryToolbar();

	/*
	CXTPToolBar *pParticleToolBar = GetCommandBars()->Add( _T("ParticlesToolBar"),xtpBarTop );
	pParticleToolBar->EnableCustomization(FALSE);
	VERIFY(pParticleToolBar->LoadToolBar( nToolbarResID ));
	CXTPToolBar *pItemBar = GetCommandBars()->GetToolBar(IDR_DB_LIBRARY_ITEM_BAR);
	if (pItemBar)
		DockRightOf(pParticleToolBar,pItemBar);
	*/
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnSize(UINT nType, int cx, int cy)
{
	__super::OnSize(nType,cx,cy);
	// resize splitter window.
	if (m_wndVSplitter.m_hWnd)
	{
		/*
		int cxCur,cxMin;
		m_wndVSplitter.GetColumnInfo(0,cxCur,cxMin);
		int nSize = max(cx-cxCur,0);
		m_wndHSplitter.SetRowInfo( 0,nSize,100 );
		*/

		CRect rc;
		GetClientRect(rc);
		m_wndVSplitter.MoveWindow(rc,FALSE);
	}
	RecalcLayout();
}

//////////////////////////////////////////////////////////////////////////
HTREEITEM CParticleDialog::InsertItemToTree( CBaseLibraryItem *pItem,HTREEITEM hParent )
{
	CParticleItem *pParticles = (CParticleItem*)pItem;

	if (pParticles->GetParent())
	{
		if (!hParent || hParent == TVI_ROOT || m_treeCtrl.GetItemData(hParent) == 0)
			return 0;
	}

	HTREEITEM hItem = CBaseLibraryDialog::InsertItemToTree( pItem,hParent );
	UpdateItemState(pParticles);

	for (int i = 0; i < pParticles->GetChildCount(); i++)
	{
		CParticleItem *pSubItem = pParticles->GetChild(i);
		InsertItemToTree( pSubItem, hItem );
	}
	return hItem;
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnAddItem()
{
	if (!m_pLibrary)
		return;

	CStringGroupDlg dlg( _T("New particle effect name"), this );
	dlg.SetGroup( m_pCurrentItem ? m_pCurrentItem->GetName() : m_selectedGroup );
	if (dlg.DoModal() != IDOK || dlg.GetString().IsEmpty())
		return;

	CString fullName = m_pItemManager->MakeFullItemName( m_pLibrary,dlg.GetGroup(),dlg.GetString() );
	if (m_pItemManager->FindItemByName( fullName ))
	{
		Warning( "Item with name %s already exist",(const char*)fullName );
		return;
	}

	CUndo undo("Add particle library item");
	CParticleItem* pParticles = (CParticleItem*)m_pItemManager->CreateItem( m_pLibrary );
	if (!dlg.GetGroup().IsEmpty())
	{
		CString parentName = m_pLibrary->GetName() + "." + dlg.GetGroup();
		if (CParticleItem* pParent = (CParticleItem*)m_pPartManager->FindItemByName(parentName))
			pParticles->SetParent(pParent);
	}
	SetItemName( pParticles,dlg.GetGroup(),dlg.GetString() );
	pParticles->SetDefaults();
	pParticles->Update();

	ReloadItems();
	SelectItem( pParticles );
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::SelectItem( CBaseLibraryItem *item,bool bForceReload )
{
	bool bChanged = item != m_pCurrentItem || bForceReload;
	m_propsCtrl.Flush();
	CBaseLibraryDialog::SelectItem( item,bForceReload );
	
	if (!item)
	{
		m_propsCtrl.EnableWindow(FALSE);
		return;
	}

	if (!bChanged)
		return;

	if (m_propsCtrl.IsWindowEnabled() != TRUE)
		m_propsCtrl.EnableWindow(TRUE);

	m_propsCtrl.EnableUpdateCallback(false);

	// Update variables.
	m_propsCtrl.EnableUpdateCallback(false);
	pParticleUI->SetFromParticles( GetSelectedParticle() );
	m_propsCtrl.EnableUpdateCallback(true);

	m_propsCtrl.SetUpdateCallback( functor(*this,&CParticleDialog::OnUpdateProperties) );
	m_propsCtrl.EnableUpdateCallback(true);

	if(m_bForceReloadPropsCtrl)
	{
		m_propsCtrl.ReloadItems();
		m_propsCtrl.ShowWindow(SW_HIDE);
		m_propsCtrl.ShowWindow(SW_SHOW);
		m_bForceReloadPropsCtrl = false;
	}
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::Update()
{
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnUpdateProperties( IVariable *var )
{
	CParticleItem *pParticles = GetSelectedParticle();
	if (!pParticles)
		return;

	pParticleUI->SetToParticles( pParticles );

	// Update visual cues of item and parents.
	CParticleItem *poEditedParticleItem(pParticles);
	for (; pParticles; pParticles = pParticles->GetParent())
	{
		UpdateItemState(pParticles);
		poEditedParticleItem=pParticles;
	}

	GetIEditor()->GetConsoleSync()->OnParticlesUpdated(poEditedParticleItem);

	GetIEditor()->SetModifiedFlag();
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnPlay()
{
	m_bRealtimePreviewUpdate = !m_bRealtimePreviewUpdate;
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnUpdatePlay( CCmdUI* pCmdUI )
{
	if (m_bRealtimePreviewUpdate)
		pCmdUI->SetCheck(TRUE);
	else
		pCmdUI->SetCheck(FALSE);
}

//////////////////////////////////////////////////////////////////////////
CParticleItem* CParticleDialog::GetSelectedParticle()
{
	CBaseLibraryItem *pItem = m_pCurrentItem;
	return (CParticleItem*)pItem;
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnAssignParticleToSelection()
{
	CParticleItem *pParticles = GetSelectedParticle();
	if (!pParticles)
		return;

	CUndo undo( "Assign ParticleEffect" );

	CSelectionGroup *pSel = GetIEditor()->GetSelection();
	if (!pSel->IsEmpty())
	{
		for (int i = 0; i < pSel->GetCount(); i++)
		{
			AssignParticleToEntity( pParticles,pSel->GetObject(i) );
		}
	}
}


//////////////////////////////////////////////////////////////////////////
void CParticleDialog::Enable(bool bAll)
{
	CParticleItem *pParticles = GetSelectedParticle();
	if (!pParticles)
		return;

	CUndo undo("Enable/Disable Effect");
	if (bAll)
		pParticles->DebugEnable( !pParticles->GetEnabledState() );
	else
		pParticles->GetEffect()->SetEnabled( !(pParticles->GetEnabledState()&1) );

	// Update variables.
	m_propsCtrl.EnableUpdateCallback(false);
	pParticleUI->SetFromParticles( pParticles );
	m_propsCtrl.EnableUpdateCallback(true);

	m_propsCtrl.SetUpdateCallback( functor(*this,&CParticleDialog::OnUpdateProperties) );
	m_propsCtrl.EnableUpdateCallback(true);

	UpdateItemState(pParticles, bAll);
	pParticles->Update();
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnSelectAssignedObjects()
{
	//@FIXME
	/*
	CParticleItem *pParticles = GetSelectedParticle();
	if (!pParticles)
		return;

	CBaseObjectsArray objects;
	GetIEditor()->GetObjectManager()->GetObjects( objects );
	for (int i = 0; i < objects.size(); i++)
	{
		CBaseObject *pObject = objects[i];
		if (pObject->GetParticle() != pParticles)
			continue;
		if (pObject->IsHidden() || pObject->IsFrozen())
			continue;
		GetIEditor()->GetObjectManager()->SelectObject( pObject );
	}
	*/
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnResetParticleOnSelection()
{
	CUndo undo( "Reset Particle" );

	//@FIXME
	/*
	CSelectionGroup *pSel = GetIEditor()->GetSelection();
	if (!pSel->IsEmpty())
	{
		for (int i = 0; i < pSel->GetCount(); i++)
		{
			pSel->GetObject(i)->SetParticle( 0 );
		}
	}
	*/
}

CEntityObject* CParticleDialog::GetItemFromEntity()
{
	CSelectionGroup *pSel = GetIEditor()->GetSelection();
	for (int i = 0; i < pSel->GetCount(); i++)
	{
		CBaseObject* pObject = pSel->GetObject(i);
		if (pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
		{
			CEntityObject *pEntity = (CEntityObject*)pObject;
			if (pEntity->GetProperties())
			{
				IVariable *pVar = pEntity->GetProperties()->FindVariable( "ParticleEffect" );
				if (pVar)
				{
					CString effect;
					pVar->Get(effect);
					{
						CBaseLibraryItem* pItem = (CBaseLibraryItem*)m_pItemManager->LoadItemByName(effect);
						if (pItem)
						{
							SelectItem(pItem);
							return pEntity;
						}
					}
				}
			}
		}
	}
	return NULL;
}

void CParticleDialog::OnGetParticleFromSelection()
{
	GetItemFromEntity();
}

void CParticleDialog::OnEntityStart()
{
}

void CParticleDialog::OnEntityStop()
{
}

//////////////////////////////////////////////////////////////////////////
bool CParticleDialog::AssignParticleToEntity( CParticleItem *pItem, CBaseObject *pObject, Vec3 const* pPos )
{
	// Assign ParticleEffect field if it has one.
	// Otherwise, spawn/attach an emitter to the entity
	assert(pItem);
	assert(pObject);
	if (pObject->IsKindOf(RUNTIME_CLASS(CEntityObject)))
	{
		CEntityObject *pEntity = (CEntityObject*)pObject;
		IVariable *pVar = 0;
		if (pEntity->GetProperties())
		{
			pVar = pEntity->GetProperties()->FindVariable( "ParticleEffect" );
			if (pVar && pVar->GetType() == IVariable::STRING)
				// Set selected entity's ParticleEffect field.
				pVar->Set( pItem->GetFullName() );
			else
			{
				// Create a new ParticleEffect entity on top of selected entity, attach to it.
				Vec3 pos;
				if (pPos)
					pos = *pPos;
				else
				{
					pos = pObject->GetPos();
					AABB box;
					pObject->GetLocalBounds(box);
					pos.z += box.max.z;
				}

				CreateParticleEntity( pItem, pos, pObject );
			}
			return true;
		}
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
CBaseObject* CParticleDialog::CreateParticleEntity( CParticleItem *pItem, Vec3 const& pos, CBaseObject* pParent )
{
	GetIEditor()->ClearSelection();
	CBaseObject* pObject = GetIEditor()->NewObject( "Entity", "ParticleEffect" );
	if (pObject)
	{
		// Set pos, offset by object size.
		AABB box;
		pObject->GetLocalBounds(box);
		pObject->SetPos(pos - Vec3(0,0,box.min.z));
		pObject->SetRotation( Quat::CreateRotationXYZ(Ang3(DEG2RAD(90),0,0)) );

		if (pParent)
			pParent->AttachChild( pObject );
		AssignParticleToEntity( pItem, pObject );
		GetIEditor()->SelectObject( pObject );
	}
	return pObject;
}

void CParticleDialog::UpdateItemState( CParticleItem* pItem, bool bRecursive )
{
	HTREEITEM hItem = stl::find_in_map( m_itemsToTree,pItem,(HTREEITEM)0 );
	if (hItem)
	{
		// Swap icon set, depending on self & child activation.
		static int nIconStates[4] = { 6, 7, 4, 5 };
		int nIconState = nIconStates[ pItem->GetEnabledState() & 3 ];
		m_treeCtrl.SetItemImage(hItem, nIconState, nIconState);
	}
	if (bRecursive)
		for (int i = 0; i < pItem->GetChildCount(); i++)
			UpdateItemState(pItem->GetChild(i), true);
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnUpdateAssignToSelection( CCmdUI* pCmdUI )
{
	if (GetSelectedParticle() && !GetIEditor()->GetSelection()->IsEmpty())
	{
		pCmdUI->Enable( TRUE );
	}
	else
	{
		pCmdUI->Enable( FALSE );
	}
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnUpdateObjectSelected( CCmdUI* pCmdUI )
{
	if (!GetIEditor()->GetSelection()->IsEmpty())
	{
		pCmdUI->Enable( TRUE );
	}
	else
	{
		pCmdUI->Enable( FALSE );
	}
}

void CParticleDialog::OnKeyDown(NMHDR* pNMHDR, LRESULT* pResult)
{
	NMTVKEYDOWN *nm = (NMTVKEYDOWN*)pNMHDR;
	if (GetAsyncKeyState(VK_CONTROL))
	{
		if (nm->wVKey == VK_SPACE)
			Enable(GetAsyncKeyState(VK_SHIFT));
	}

	CBaseLibraryDialog::OnKeyDownItemTree(pNMHDR, pResult);
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

	HTREEITEM hItem = pNMTreeView->itemNew.hItem;

	CParticleItem* pParticles = (CParticleItem*)m_treeCtrl.GetItemData(hItem);
	if (!pParticles)
		return;

	m_pDraggedItem = pParticles;

	m_treeCtrl.Select( hItem,TVGN_CARET );

	m_hDropItem = 0;
	m_dragImage = m_treeCtrl.CreateDragImage( hItem );
	if (m_dragImage)
	{
		m_hDraggedItem = hItem;
		m_hDropItem = hItem;
		m_dragImage->BeginDrag(0, CPoint(-10, -10));

		CRect rc;
		AfxGetMainWnd()->GetWindowRect( rc );
		
		CPoint p = pNMTreeView->ptDrag;
		ClientToScreen( &p );
		p.x -= rc.left;
		p.y -= rc.top;
		
		m_dragImage->DragEnter( AfxGetMainWnd(),p );
		SetCapture();
		GetIEditor()->EnableUpdate( false );
	}
	
	*pResult = 0;
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnMouseMove(UINT nFlags, CPoint point)
{
	if (m_dragImage)
	{
		CPoint p;

		p = point;
		ClientToScreen( &p );
		m_treeCtrl.ScreenToClient( &p );

		TVHITTESTINFO hit;
		ZeroStruct(hit);
		hit.pt = p;
		HTREEITEM hHitItem = m_treeCtrl.HitTest( &hit );
		if (hHitItem)
		{
			if (m_hDropItem != hHitItem)
			{
				if (m_hDropItem)
					m_treeCtrl.SetItem( m_hDropItem,TVIF_STATE,0,0,0,0,TVIS_DROPHILITED,0 );
				// Set state of this item to drop target.
				m_treeCtrl.SetItem( hHitItem,TVIF_STATE,0,0,0,TVIS_DROPHILITED,TVIS_DROPHILITED,0 );
				m_hDropItem = hHitItem;
				m_treeCtrl.Invalidate();
			}
		}

		CRect rc;
		AfxGetMainWnd()->GetWindowRect( rc );
		p = point;
		ClientToScreen( &p );
		p.x -= rc.left;
		p.y -= rc.top;
		m_dragImage->DragMove( p );

		SetCursor( m_hCursorDefault );
		// Check if can drop here.
		{
			CPoint p;
			GetCursorPos( &p );
			CViewport* viewport = GetIEditor()->GetViewManager()->GetViewportAtPoint( p );
			if (viewport)
			{
				CPoint vp = p;
				viewport->ScreenToClient(&vp);
				HitContext hit;
				if (viewport->HitTest( vp,hit ))
				{
					if (hit.object && hit.object->IsKindOf(RUNTIME_CLASS(CEntityObject)))
					{
						SetCursor( m_hCursorReplace );
					}
				}
			}
		}
	}

	CBaseLibraryDialog::OnMouseMove(nFlags, point);
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnLButtonUp(UINT nFlags, CPoint point)
{
	//CXTResizeDialog::OnLButtonUp(nFlags, point);

	if (m_hDropItem)
	{
		m_treeCtrl.SetItem( m_hDropItem,TVIF_STATE,0,0,0,0,TVIS_DROPHILITED,0 );
		m_hDropItem = 0;
	}

	if (m_dragImage)
	{
		CPoint p;
		GetCursorPos( &p );

		GetIEditor()->EnableUpdate( true );

		m_dragImage->DragLeave( AfxGetMainWnd() );
		m_dragImage->EndDrag();
		delete m_dragImage;
		m_dragImage = 0;
		ReleaseCapture();

		CPoint treepoint = p;
		m_treeCtrl.ScreenToClient( &treepoint );

		CRect treeRc;
		m_treeCtrl.GetClientRect(treeRc);

		if (treeRc.PtInRect(treepoint))
		{
			// Dropped inside tree.
			TVHITTESTINFO hit;
			ZeroStruct(hit);
			hit.pt = treepoint;
			HTREEITEM hHitItem = m_treeCtrl.HitTest( &hit );
			if (hHitItem)
			{
				DropToItem( hHitItem,m_hDraggedItem,m_pDraggedItem );
				m_hDraggedItem = 0;
				m_pDraggedItem = 0;
				return;
			}
			DropToItem( 0,m_hDraggedItem,m_pDraggedItem );
		}
		else
		{
			// Not dropped inside tree.

			CWnd *wnd = WindowFromPoint( p );

			CUndo undo( "Assign ParticleEffect" );

			CViewport* viewport = GetIEditor()->GetViewManager()->GetViewportAtPoint( p );
			if (viewport)
			{
				CPoint vp = p;
				viewport->ScreenToClient(&vp);
				CParticleItem *pParticles = m_pDraggedItem;

				// Drag and drop into one of views.
				HitContext  hit;
				if (viewport->HitTest( vp,hit ))
				{
					Vec3 hitpos = hit.raySrc + hit.rayDir * hit.dist;
					if (hit.object && AssignParticleToEntity( pParticles, hit.object, &hitpos ))
						; // done
					else
					{
						// Place emitter at hit location.
						hitpos = viewport->SnapToGrid(hitpos);
						CreateParticleEntity(pParticles, hitpos);
					}
				}
				else
				{
					// Snap to terrain.
					bool hitTerrain;
					Vec3 pos = viewport->ViewToWorld( vp,&hitTerrain );
					if (hitTerrain)
					{
						pos.z = GetIEditor()->GetTerrainElevation(pos.x,pos.y);
					}
					pos = viewport->SnapToGrid(pos);
					CreateParticleEntity(pParticles, pos);
				}
			}
		}
		m_pDraggedItem = 0;
	}
	m_pDraggedItem = 0;
	m_hDraggedItem = 0;

	CBaseLibraryDialog::OnLButtonUp(nFlags, point);
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnNotifyTreeRClick(NMHDR* pNMHDR, LRESULT* pResult)
{
	// Show helper menu.
	CPoint point;

	CParticleItem *pParticles = 0;

	// Find node under mouse.
	GetCursorPos( &point );
	m_treeCtrl.ScreenToClient( &point );
	// Select the item that is at the point myPoint.
	UINT uFlags;
	HTREEITEM hItem = m_treeCtrl.HitTest(point,&uFlags);
	if ((hItem != NULL) && (TVHT_ONITEM & uFlags))
	{
		pParticles = (CParticleItem*)m_treeCtrl.GetItemData(hItem);
	}

	if (!pParticles)
		return;

	SelectItem( pParticles );

	// Create pop up menu.
	CMenu menu;
	menu.CreatePopupMenu();
	
	if (pParticles)
	{
		CClipboard clipboard;
		bool bNoPaste = clipboard.IsEmpty();
		int pasteFlags = 0;
		if (bNoPaste)
			pasteFlags |= MF_GRAYED;

		menu.AppendMenu( MF_STRING,ID_DB_CUT,"Cut" );
		menu.AppendMenu( MF_STRING,ID_DB_COPY,"Copy" );
		menu.AppendMenu( MF_STRING|pasteFlags,ID_DB_PASTE,"Paste" );
		menu.AppendMenu( MF_STRING,ID_DB_CLONE,"Clone" ); 
		menu.AppendMenu( MF_SEPARATOR,0,"" );
		menu.AppendMenu( MF_STRING,ID_DB_RENAME,"Rename" );
		menu.AppendMenu( MF_STRING,ID_DB_REMOVE,"Delete" );
		menu.AppendMenu( MF_STRING,ID_DB_ACTIVATE, pParticles->GetEnabledState() & 1 ? "Disable (Ctrl-Space)" : "Enable (Ctrl-Space)" );
		menu.AppendMenu( MF_STRING,ID_DB_ACTIVATE_ALL, pParticles->GetEnabledState() ? "Disable All (Ctrl-Shift-Space)" : "Enable All (Ctrl-Shift-Space)" );
		menu.AppendMenu( MF_SEPARATOR,0,"" );
		menu.AppendMenu( MF_STRING,ID_DB_ASSIGNTOSELECTION,"Assign to Selected Objects" );
	}

	GetCursorPos( &point );
	menu.TrackPopupMenu( TPM_LEFTALIGN|TPM_LEFTBUTTON,point.x,point.y,this );
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnNotifyTreeLClick(NMHDR* pNMHDR, LRESULT* pResult)
{
	*pResult = FALSE;
	
	// Show helper menu.
	CPoint point;

	CParticleItem *pParticles = 0;

	// Find node under mouse.
	GetCursorPos( &point );
	m_treeCtrl.ScreenToClient( &point );
	// Select the item that is at the point myPoint.
	UINT uFlags;
	HTREEITEM hItem = m_treeCtrl.HitTest(point,&uFlags);
	if ((hItem != NULL) && (TVHT_ONITEM & uFlags))
	{
		pParticles = (CParticleItem*)m_treeCtrl.GetItemData(hItem);
	}

	if (pParticles)
		m_previewCtrl.LoadParticleEffect( pParticles->GetEffect() );
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnPick()
{
	if (!CParticlePickCallback::IsActive())
		GetIEditor()->PickObject( new CParticlePickCallback,0,"Pick Object to Select Particle" );
	else
		GetIEditor()->CancelPick();
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnUpdatePick( CCmdUI* pCmdUI )
{
	if (CParticlePickCallback::IsActive())
	{
		pCmdUI->SetCheck(1);
	}
	else
	{
		pCmdUI->SetCheck(0);
	}
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnCopy()
{
	CParticleItem *pParticles = GetSelectedParticle();
	if (pParticles)
	{
		XmlNodeRef node = XmlHelpers::CreateXmlNode( "Particles" );
		CBaseLibraryItem::SerializeContext ctx(node,false);
		ctx.bCopyPaste = true;

		CClipboard clipboard;
		pParticles->Serialize( ctx );
		clipboard.Put( node );
	}
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::OnPaste()
{
	if (!m_pLibrary)
		return;

	CParticleItem *pItem = GetSelectedParticle();
	if (!pItem)
		return;

	CClipboard clipboard;
	if (clipboard.IsEmpty())
		return;
	XmlNodeRef node = clipboard.Get();
	if (!node)
		return;

	if (strcmp(node->getTag(),"Particles") == 0)
	{
		CUndo undo("Add particle library item");
		node->delAttr( "Name" );

		m_pPartManager->PasteToParticleItem( pItem,node,true );
		ReloadItems();
		SelectItem(pItem);
	}
}

//////////////////////////////////////////////////////////////////////////
void CParticleDialog::DropToItem( HTREEITEM hItem,HTREEITEM hSrcItem,CParticleItem *pParticles )
{
	pParticles->GetLibrary()->SetModified();

	TSmartPtr<CParticleItem> pHolder = pParticles; // Make usre its not release while we remove and add it back.

	if (!hItem)
	{
		// Detach from parent.
		pParticles->SetParent(NULL);

		ReloadItems();
		SelectItem( pParticles );
		return;
	}

	CParticleItem* pTargetItem = (CParticleItem*)m_treeCtrl.GetItemData(hItem);
	if (!pTargetItem)
	{
		// This is group.
		
		// Detach from parent.
		pParticles->SetParent(NULL);

		// Move item to different group.
		CString groupName = GetItemFullName(hItem, ".");
		SetItemName( pParticles,groupName,pParticles->GetShortName() );

		m_treeCtrl.DeleteItem( hSrcItem );
		InsertItemToTree( pParticles,hItem );
		return;
	}

	// Ignore itself.
	if (pTargetItem == pParticles)
		return;

	// Move to new parent.
	pParticles->SetParent(pTargetItem);

	ReloadItems();
	SelectItem( pParticles );
}
